import time
import threading
from typing import Dict, Any, Optional, Callable
from datetime import datetime, timedelta
import logging

class UnifiedCacheManager:
    """
    统一缓存管理器
    为所有指标提供统一的缓存机制，减少重复计算
    """
    
    def __init__(self):
        self.cache = {}
        self.cache_metadata = {}
        self.lock = threading.RLock()
        
        # 默认缓存配置
        self.default_ttl = {
            'macd': 60,        # MACD缓存60秒
            'rsi': 30,         # RSI缓存30秒
            'kdj': 30,         # KDJ缓存30秒
            'vma': 60,         # VMA缓存60秒
            'updown': 10,      # 涨跌幅缓存10秒
            'nine_turn': 5,    # 九转缓存5秒
            'atr': 120,        # ATR缓存2分钟
            'volume': 30       # 成交量缓存30秒
        }
        
        # 缓存统计
        self.stats = {
            'hits': 0,
            'misses': 0,
            'evictions': 0,
            'total_requests': 0
        }
        
    def get_cache_key(self, stock_code: str, indicator_type: str, **kwargs) -> str:
        """生成缓存键"""
        # 如果有额外参数，将其排序后加入键中
        if kwargs:
            sorted_kwargs = sorted(kwargs.items())
            param_str = "_".join([f"{k}={v}" for k, v in sorted_kwargs])
            return f"{stock_code}_{indicator_type}_{param_str}"
        return f"{stock_code}_{indicator_type}"
        
    def get(self, stock_code: str, indicator_type: str, **kwargs) -> Optional[Any]:
        """获取缓存值"""
        with self.lock:
            cache_key = self.get_cache_key(stock_code, indicator_type, **kwargs)
            self.stats['total_requests'] += 1
            
            if cache_key not in self.cache:
                self.stats['misses'] += 1
                return None
                
            # 检查缓存是否过期
            metadata = self.cache_metadata.get(cache_key, {})
            ttl = metadata.get('ttl', self.default_ttl.get(indicator_type, 60))
            created_time = metadata.get('created_time', 0)
            
            if time.time() - created_time > ttl:
                # 缓存过期，删除
                self._evict_cache(cache_key)
                self.stats['misses'] += 1
                return None
                
            self.stats['hits'] += 1
            return self.cache[cache_key]
            
    def set(self, stock_code: str, indicator_type: str, value: Any, ttl: Optional[int] = None, **kwargs):
        """设置缓存值"""
        with self.lock:
            cache_key = self.get_cache_key(stock_code, indicator_type, **kwargs)
            
            # 使用指定的TTL或默认TTL
            if ttl is None:
                ttl = self.default_ttl.get(indicator_type, 60)
                
            self.cache[cache_key] = value
            self.cache_metadata[cache_key] = {
                'created_time': time.time(),
                'ttl': ttl,
                'indicator_type': indicator_type,
                'stock_code': stock_code
            }
            
    def get_or_compute(self, stock_code: str, indicator_type: str, 
                      compute_func: Callable, ttl: Optional[int] = None, **kwargs) -> Any:
        """获取缓存值，如果不存在则计算并缓存"""
        # 先尝试从缓存获取
        cached_value = self.get(stock_code, indicator_type, **kwargs)
        if cached_value is not None:
            return cached_value
            
        # 缓存未命中，执行计算
        try:
            computed_value = compute_func()
            self.set(stock_code, indicator_type, computed_value, ttl, **kwargs)
            return computed_value
        except Exception as e:
            logging.error(f"计算指标失败 {stock_code} {indicator_type}: {e}")
            return None
            
    def _evict_cache(self, cache_key: str):
        """删除指定缓存"""
        if cache_key in self.cache:
            del self.cache[cache_key]
        if cache_key in self.cache_metadata:
            del self.cache_metadata[cache_key]
        self.stats['evictions'] += 1
        
    def cleanup_expired(self):
        """清理过期缓存"""
        with self.lock:
            current_time = time.time()
            expired_keys = []
            
            for cache_key, metadata in self.cache_metadata.items():
                ttl = metadata.get('ttl', 60)
                created_time = metadata.get('created_time', 0)
                
                if current_time - created_time > ttl:
                    expired_keys.append(cache_key)
                    
            for cache_key in expired_keys:
                self._evict_cache(cache_key)
                
    def clear_stock_cache(self, stock_code: str):
        """清理指定股票的所有缓存"""
        with self.lock:
            keys_to_remove = []
            for cache_key in self.cache.keys():
                if cache_key.startswith(f"{stock_code}_"):
                    keys_to_remove.append(cache_key)
                    
            for cache_key in keys_to_remove:
                self._evict_cache(cache_key)
                
    def clear_indicator_cache(self, indicator_type: str):
        """清理指定指标类型的所有缓存"""
        with self.lock:
            keys_to_remove = []
            for cache_key in self.cache.keys():
                if f"_{indicator_type}_" in cache_key or cache_key.endswith(f"_{indicator_type}"):
                    keys_to_remove.append(cache_key)
                    
            for cache_key in keys_to_remove:
                self._evict_cache(cache_key)
                
    def clear_all_cache(self):
        """清理所有缓存"""
        with self.lock:
            self.cache.clear()
            self.cache_metadata.clear()
            
    def get_cache_stats(self) -> Dict[str, Any]:
        """获取缓存统计信息"""
        with self.lock:
            total_requests = self.stats['total_requests']
            hit_rate = (self.stats['hits'] / total_requests * 100) if total_requests > 0 else 0
            
            return {
                'cache_size': len(self.cache),
                'total_requests': total_requests,
                'hits': self.stats['hits'],
                'misses': self.stats['misses'],
                'evictions': self.stats['evictions'],
                'hit_rate': f"{hit_rate:.2f}%"
            }
            
    def get_stock_cache_info(self, stock_code: str) -> Dict[str, Any]:
        """获取指定股票的缓存信息"""
        with self.lock:
            stock_caches = {}
            for cache_key, value in self.cache.items():
                if cache_key.startswith(f"{stock_code}_"):
                    metadata = self.cache_metadata.get(cache_key, {})
                    created_time = metadata.get('created_time', 0)
                    ttl = metadata.get('ttl', 60)
                    age = time.time() - created_time
                    
                    stock_caches[cache_key] = {
                        'value': value,
                        'age_seconds': age,
                        'ttl_seconds': ttl,
                        'expires_in': max(0, ttl - age)
                    }
            return stock_caches
            
    def set_ttl(self, indicator_type: str, ttl: int):
        """设置指定指标类型的默认TTL"""
        self.default_ttl[indicator_type] = ttl
        
    def get_ttl(self, indicator_type: str) -> int:
        """获取指定指标类型的默认TTL"""
        return self.default_ttl.get(indicator_type, 60)
        
    def start_cleanup_thread(self, interval: int = 300):
        """启动定期清理线程（默认5分钟清理一次）"""
        def cleanup_worker():
            while True:
                time.sleep(interval)
                self.cleanup_expired()
                
        cleanup_thread = threading.Thread(target=cleanup_worker, daemon=True)
        cleanup_thread.start()
        return cleanup_thread
